package com.wang.api;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

public class ApiGenerator {
    public static class ApiFile {
        public String urlPre;
        public File apiFile;
        public FileOutputStream fos;
    }

    public static class ApiInfo {
        public String module;
        public String urlPath;
        public String method;
        public Class req;
        public Class response;

    }

    /**
     * JSONObject.toJSONString(
     * req, SerializerFeature.PrettyFormat, SerializerFeature.WriteMapNullValue,
     * SerializerFeature.WriteDateUseDateFormat
     * )
     */

    public static ClassLoader clzLoader = ClassLoader.getSystemClassLoader();

    public static Map<String, Class> allClazz(File dir, String pkg) throws ClassNotFoundException {
        Map<String, Class> map = new HashMap<>();
        for (File sub : dir.listFiles()) {
            if (sub.isDirectory()) {
                map.putAll(allClazz(sub, pkg + '.' + sub.getName()));
            } else if (sub.getName().endsWith(".class")) {
                String clzName = sub.getName().substring(0, sub.getName().indexOf('.'));
                String clzAllName = pkg + "." + clzName;
                Class clz = clzLoader.loadClass(clzAllName);
                map.put(clzAllName, clz);
            } else {
                continue;
            }
        }
        return map;
    }

    public static Map<String, Class> controllerClz(Map<String, Class> clzMap) {
        Map<String, Class> map = new HashMap<>();
        clzMap.forEach((k, v) -> {
            if (k.endsWith("Controller")) {
                map.put(k, v);
            }
        });
        return map;
    }

    private static List<ApiInfo> controllerToApiDocument(Class controller) {
        Function<Method, String[]> mapping = m -> {
            RequestMapping r = m.getAnnotation(RequestMapping.class);
            if (r != null) return new String[]{r.value()[0], r.method().length == 0 ? "post" : r.method()[0].name()};
            PostMapping p = m.getAnnotation(PostMapping.class);
            if (p != null) return new String[]{p.value()[0], "post"};
            GetMapping g = m.getAnnotation(GetMapping.class);
            if (g != null) return new String[]{g.value()[0], "get"};
            return null;
        };

        String module = ((RequestMapping) (controller.getAnnotation(RequestMapping.class))).value()[0];
        return Arrays.stream(controller.getDeclaredMethods())
                .filter(method -> mapping.apply(method) != null)
                .map(method -> {
                    String[] urlAndMethod = mapping.apply(method);
                    String urlPath = module + urlAndMethod[0];
                    Class req = method.getParameters().length == 0 ? null : method.getParameters()[0].getType();
                    Class response = method.getReturnType();
                    ApiInfo info = new ApiInfo();
                    info.method = urlAndMethod[1];
                    info.module = module;
                    info.req = req;
                    info.response = response;
                    info.urlPath = urlPath;
                    return info;
                }).collect(Collectors.toList());
    }



    public static ApiFile[] apiFiles = new ApiFile[3];
    static {
        try {
            ApiFile localApiFile = new ApiFile();
            localApiFile.urlPre = "http://localhost:9606";
            localApiFile.apiFile = new File("./wave-modules/wave-landlord-service/src/landlord-localhost.http");
            localApiFile.apiFile.createNewFile();
            localApiFile.fos = new FileOutputStream(localApiFile.apiFile);
            apiFiles[0] = localApiFile;

            ApiFile devFile = new ApiFile();
            devFile.urlPre = "http://10.0.14.57:6060/landlord-service";
            devFile.apiFile = new File("./wave-modules/wave-landlord-service/src/landlord-dev.http");
            devFile.apiFile.createNewFile();
            devFile.fos = new FileOutputStream(devFile.apiFile);
            apiFiles[1] = devFile;

            ApiFile testFile = new ApiFile();
            testFile.urlPre = "http://test-wx.wavewisdom.com/landlord-service";
            testFile.apiFile = new File("./wave-modules/wave-landlord-service/src/landlord-test.http");
            testFile.apiFile.createNewFile();
            testFile.fos = new FileOutputStream(testFile.apiFile);
            apiFiles[2] = testFile;
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static Object baseType(Class clz) {
        if(clz.equals(Integer.class)) return new Integer(0);
        if(clz.equals(Double.class)) return new Double(0);
        if(clz.equals(Float.class)) return new Float(0);
        if(clz.equals(String.class)) return "";
        return null;
    }

    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, IOException {

        String clzDir = "/target/classes/com";
        String targetClassDir = "./wave-modules/wave-landlord-service/" + clzDir;
        Map<String, Class> map = allClazz(new File(targetClassDir), "com");
        List<ApiInfo> list = controllerClz(map).values().stream()
                .map(ApiGenerator::controllerToApiDocument)
                .flatMap(List::stream)
                .collect(Collectors.toList());

        for (ApiInfo info : list) {
            Object instance = null;
            if(info.req == null) {
                instance = null;
            } else if(info.req.isArray()) {
                List l = new ArrayList(1);
                l.add(info.req.getSuperclass().newInstance());
                instance = l;
            } else if (info.req.equals(List.class)) {
                // 类型擦除，无法获取具体泛型参数
                instance = new ArrayList();
            } else if((instance = baseType(info.req)) != null){
                // 基础数据类型
                instance = baseType(info.req);
            } else {
                instance = info.req.newInstance();
            }

            String rs = JSONObject.toJSONString(
                    instance,
                    SerializerFeature.PrettyFormat,
                    SerializerFeature.WriteMapNullValue,
                    SerializerFeature.WriteDateUseDateFormat);

            for (ApiFile apiFile : apiFiles) {
                String s = info.method.toUpperCase() + " " + apiFile.urlPre + info.urlPath + "\n" +
                        "Content-Type: application/json\n" +
                        "accessToken: acef75fd-f198-442c-ae98-8b16f84f964a\n" +
                        "\n" + rs + "\n###\n";
                apiFile.fos.write(s.getBytes());
            }
        }

        for (ApiFile apiFile : apiFiles) {
            apiFile.fos.flush();
            apiFile.fos.close();
        }

        System.out.println(list);
    }
}
